package com.niuniu.rushdorm.controller;

/**
 * @author Meng
 */

import com.niuniu.rushdorm.exception.ReportedException;
import com.niuniu.rushdorm.pojo.vo.UserVO;
import com.niuniu.rushdorm.pojo.vo.ResultVO;
import com.niuniu.rushdorm.service.CredentialService;
import com.niuniu.rushdorm.service.StudentService;
import com.niuniu.rushdorm.service.UserService;
import com.niuniu.rushdorm.util.JwtUtils;
import com.niuniu.rushdorm.util.StatusEnum;
import com.niuniu.rushdorm.util.UUIDUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
// test
@RestController
@RequestMapping(value = "/auth",produces = "application/json;charset=utf-8")
public class AuthController {
    @Autowired
    CredentialService credentialService;
    @Autowired
    UserService userService;
    @Autowired
    StudentService studentService;

    @Autowired
    RedisTemplate redisTemplate;

    @Value("${customization.token.access-token-expire}")
    private int accessTokenExpireSeconds;
    @Value("${customization.token.refresh-token-expire}")
    private int refreshTokenExpireSeconds;
    //使用refresh_token更新token的次数限制，超过该次数需要重新登录
    @Value("${customization.token.refresh-limit}")
    private int refreshLimit;


    @PostMapping("/login")
    ResultVO login(HttpServletRequest request,@RequestBody Map<String,String> map){
        Long start = System.currentTimeMillis();

        //验证身份
        String username = map.get("username");
        String password = map.get("password");
        Long uid = credentialService.verifyCredential(username,password);

        Long e1 = System.currentTimeMillis();
        //System.out.println("验证密码用时"+(e1-start));

        UserVO userVO = userService.getUser(uid);
        if(userVO==null){
            throw ReportedException.build(StatusEnum.INVALID_USER);
        }

        //创建token
        String accessTokenId = UUIDUtils.uuid();
        String refreshTokenId = UUIDUtils.uuid();
        String accessToken = JwtUtils.generateToken(uid,accessTokenId,0,userVO);
        String refreshToken = JwtUtils.generateToken(uid,refreshTokenId,0,userVO);


        Long e2 = System.currentTimeMillis();
        //System.out.println("生成Token用时"+(e2-e1));

        //保存token到redis中
        redisTemplate.opsForValue().set("access_token:"+String.valueOf(uid),accessTokenId,accessTokenExpireSeconds, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set("refresh_token:"+String.valueOf(uid),refreshTokenId,refreshTokenExpireSeconds,TimeUnit.SECONDS);
        //redisTemplate.opsForValue().set("used_token:"+accessTokenId,1,2*accessTokenExpireSeconds,TimeUnit.SECONDS);

        //构造返回data
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("access_token",accessToken);
        dataMap.put("refresh_token",refreshToken);
        dataMap.put("expires_in",accessTokenExpireSeconds);
        dataMap.put("token_type","bearer");
        dataMap.put("scope","web");
        userVO.setLastLoginTime(System.currentTimeMillis()/1000);

        Long e3 = System.currentTimeMillis();
        //System.out.println("写缓存用时"+(e3-e2));

        //userService.updateLoginTime(userVO);

        Long e4 = System.currentTimeMillis();
        //System.out.println("总用时"+(e4-start));

        request.setAttribute("uid",uid);
        return ResultVO.success(dataMap);
    }

    @PostMapping("/refresh_token")
    ResultVO refreshToken(HttpServletRequest request,@RequestBody Map<String,String> map){
        String refresh_token = map.get("refresh_token");
        //解析refresh_token
        Claims claims = null;
        try{
            claims = JwtUtils.getClaims(refresh_token);
        }catch (SignatureException e){
            throw ReportedException.build(StatusEnum.ILLEGAL_TOKEN);
        }
        Long uid = claims.get("uid",Long.class);
        String oldTokenId = claims.get("token_id",String.class);
        Integer refreshCount = claims.get("refresh_count",Integer.class);

        if(refreshCount>=refreshLimit){
            throw ReportedException.build(StatusEnum.REFRESH_LIMITED);
        }

        //从redis中取出用户refresh_token的id
        String cachedId = (String)redisTemplate.opsForValue().get("refresh_token:"+String.valueOf(uid));


        //如果redis中没有refresh_token,或不匹配，说明请求中的token无效
        if(cachedId==null||!cachedId.equals(oldTokenId)){
            throw ReportedException.build(StatusEnum.UNVALID_REFRESH_TOKEN);
        }

        //创建新的access_token和refresh_token
        String accessTokenId = UUIDUtils.uuid();
        String refreshTokenId = UUIDUtils.uuid();
        UserVO userVO  = JwtUtils.getUserVO(refresh_token);
        String accessToken = JwtUtils.generateToken(uid,accessTokenId,refreshCount+1,userVO);
        String refreshToken = JwtUtils.generateToken(uid,refreshTokenId,refreshCount+1,userVO);

        //保存新的token到redis中
        redisTemplate.opsForValue().set("access_token:"+String.valueOf(uid),accessTokenId,accessTokenExpireSeconds, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set("refresh_token:"+String.valueOf(uid),refreshTokenId,refreshTokenExpireSeconds,TimeUnit.SECONDS);
        //redisTemplate.opsForValue().set("used_token:"+accessTokenId,1,2*accessTokenExpireSeconds,TimeUnit.SECONDS);

        //构建返回data
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("access_token",accessToken);
        dataMap.put("refresh_token",refreshToken);
        dataMap.put("expires_in",accessTokenExpireSeconds);
        dataMap.put("token_type","bearer");
        dataMap.put("scope","web");

        request.setAttribute("uid",uid);
        return ResultVO.success(dataMap);
    }

    @PostMapping("/logout")
    ResultVO logout(HttpServletRequest request){
        UserVO userVO = (UserVO)request.getAttribute("userVO");
        Long uid = userVO.getUid();
        redisTemplate.delete("access_token:"+String.valueOf(uid));
        redisTemplate.delete("refresh_token:"+String.valueOf(uid));
        return ResultVO.success(null);
    }
}

